package de.ret_world.openrpgstudio.editor;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Properties;
import org.apache.log4j.Logger;

/**
 * Singleton for main application configuration access and storage for all plugins
 *
 * @author Emu
 */
public class Configuration {

    private Properties config;
    private Path configPath;
    private String plugin;

    private Configuration(String plugin) {
        config = new Properties();
        this.plugin = plugin;
    }
    /**
     * dataFolder is set through main application at initialization. changing causes major bugs!
     */
    protected static Path dataFolder;

    /**
     * Returns path to folder where given plug-in can store it's data.
     * @param descr plug-in descriptor
     * @return plug-in data folder
     * @throws IOException if folder doesn't exist and can't be created
     */
    public static Path getDataFolder(String ID) {
        Path result = dataFolder.resolve(ID);
        try {
            Files.createDirectories(result);
        } catch (IOException ex) {
            Logger.getLogger(Configuration.class).error(ex);
        }
        return result;
    }

    /**
     * @return the single instance of the class
     */
    public static Configuration getInstance(String ID) {
        if (ConfigurationHolder.INSTANCES == null) {
            ConfigurationHolder.INSTANCES = new HashMap<>();
        }

        if (ConfigurationHolder.INSTANCES.containsKey(ID)) {
            return ConfigurationHolder.INSTANCES.get(ID);
        } else {
            ConfigurationHolder.INSTANCES.put(ID, new Configuration(ID));
            Configuration c = ConfigurationHolder.INSTANCES.get(ID);
            c.initConfig(ID);
            return c;
        }
    }

    private static class ConfigurationHolder {

        private static HashMap<String, Configuration> INSTANCES = new HashMap<>();
    }

    /**
     * Initializes the configuration file by loading the saved properties file. If no file is found (first start of the application) a default will be set.
     * @param configPath the path to the properties file.
     */
    private void initConfig(String ID) {
        Logger.getLogger(Configuration.class).info("Initializing configuration system for plugin " + ID);
        this.configPath = getDataFolder(ID).resolve("plugin.config.properties");
        if (Files.exists(configPath)) {
            try {
                config.load(Files.newInputStream(configPath));
            } catch (IOException ex) {
                Logger.getLogger(Configuration.class).error(ex);
            }
        } else {
            try {
                config.store(Files.newOutputStream(configPath), "OpenRPGStudio Config\nPlugin: " + plugin);
            } catch (IOException ex) {
                Logger.getLogger(Configuration.class).error(ex);
            }
        }
    }

    /**
     * Calls Properties.getProperty(). You can set a default value to use if no property is found using the given key, otherwise the return would be null. Remember: You can set as many defaultValues as you want, but only the first argument will be used!
     */
    public String getProperty(String key, String... defaultValue) {
        if (config.containsKey(key)) {
            return config.getProperty(key);
        } else {
            if (defaultValue == null || defaultValue.length == 0) {
                return null;
            }
            config.setProperty(key, defaultValue[0]);
            return defaultValue[0];
        }
    }

    /**
     * Calls Properties.setProperty()
     */
    public void setProperty(String key, String value) {
        config.setProperty(key, value);
    }

    /**
     * Saves the configuration to the properties file at the initialized configPath.
     */
    public void storeConfig() {
        try {
            config.store(Files.newOutputStream(configPath), "OpenRPGStudio Config\nPlugin: " + plugin);
        } catch (IOException ex) {
            Logger.getLogger(Configuration.class).error(ex);
        }
    }

    /**
     * @return returns the internal Properties object. You should only use this method if you need a special method of the Properties object.
     */
    public Properties getConfig() {
        return config;
    }
}
